import re
import calendar
import posixpath
import amara
from datetime import datetime

from dateutil.tz import *
from paste.httpexceptions import *

from brightcontent.core import SLUGCHARS
from brightcontent.util import UrlDispatch, get_base_url
from brightcontent.plugins.atomprotocol import WsgiAtomPublishingProtocol
from brightcontent.core.store import COMMON_PREFIXES


class WsgiApplication(object):
    def __init__(self, application, store, app_conf):
        self.store = store
        self.application = application
        self.m = UrlDispatch(self.urls)
        self.entries_per_page = int(app_conf.get('entries_per_page', 10))
        self.base_url = app_conf['weblog_base_url']
        # Potentially customized internal APP impl
        self.atomprotocol = WsgiAtomPublishingProtocol(application, self.store, app_conf)

    def __call__(self, environ, start_response):
        #import pprint; pprint.pprint(environ)
        path_info = environ['PATH_INFO']
        func, query_dict = self.m.match(path_info)
        # print path_info, func, query_dict
        if func:
            return func(self, environ, start_response, **query_dict)
        else:
            return self.application(environ, start_response)

    def return_entries(self, environ, start_response, entries):
        start_response('200 OK', [('Content-type', 'application/atom+xml')])
        is_atom_request = environ.get('PATH_INFO', '').endswith('.atom')
        environ['brightcontent.render'] = not is_atom_request
        if entries:
            if len(entries) == 1 and is_atom_request:
                feed = entries[0]
            else:
                feed = self.store.assemble_feed(entries)
            return [feed]
        raise HTTPNotFound()

    #y/m/d are keyword params just so that we don't get unexpected keyword errors
    #But this function must *always* be called with all three specified.  slug is optional
    def specific(self, environ, start_response, year=0, month=0, day=0, slug=None, **etc):
        if environ['REQUEST_METHOD'] == 'PUT':
            return self.atomprotocol(environ, start_response)
        year, month, day = int(year), int(month), int(day)
        lower_date = datetime(year, month, day, 0, 0, 0, tzinfo=tzutc()) 
        upper_date = datetime(year, month, day, 23, 59, 59, tzinfo=tzutc())
        if slug:
            #Going from likely string to int back to string in this way seems inefficient
            #But this last bit's at C speed, so it's OK
            entry_id = posixpath.join(self.base_url, lower_date.strftime('%Y/%m/%d'), slug + '.atom')
            print "looking for %s" % entry_id
            entries = self.store.get_entries(eid=entry_id,
                                             limit=self.entries_per_page)
            return self.return_entries(environ, start_response, entries)
        else:
            entries = self.store.get_entries(lower_date=lower_date,
                                             upper_date=upper_date, 
                                             limit=self.entries_per_page)
            if len(entries) > 1:
                return self.return_entries(environ, start_response, entries)
            print "One Entry: %s" % environ['PATH_INFO']
            new_loc = ''
            permalink = amara.pushbind(entries[0], u"atom:link", prefixes=COMMON_PREFIXES)
            for l in permalink:
                if hasattr(l, 'title') and l.title == 'permalink':
                    new_loc = l.href
            raise HTTPMovedPermanently(headers=[('location', '%s' % new_loc)])

                

    def month(self, environ, start_response, year=0, month=0, **etc):
        year, month = int(year), int(month)
        lower_date = datetime(year, month, 1, tzinfo=tzutc())
        upper_date = datetime(year, month, calendar.monthrange(year, month)[1], tzinfo=tzutc())
        entries = self.store.get_entries(lower_date=lower_date,
                                         upper_date=upper_date,
                                         limit=self.entries_per_page)
        return self.return_entries(environ, start_response, entries)

    def year(self, environ, start_response, year=0, **etc):
        year = int(year)
        lower_date = datetime(year, 1, 1, tzinfo=tzutc())
        upper_date = datetime(year, 12, 31, tzinfo=tzutc())
        entries = self.store.get_entries(lower_date=lower_date,
                                         upper_date=upper_date,
                                         limit=self.entries_per_page)
        return self.return_entries(environ, start_response, entries)

    def page(self, environ, start_response, page=1, **etc):
        limit = self.entries_per_page
        offset = (page-1) * limit
        entries = self.store.get_entries(offset=offset, limit=limit)
        return self.return_entries(environ, start_response, entries)

    def index(self, environ, start_response, **etc):
        if environ['REQUEST_METHOD'] == 'POST':
            return self.atomprotocol(environ, start_response)
        return self.page(environ, start_response, 1)

    urls = (
            #Non-slug full date check has to be on top because it's
            #most specific (think of the index.atom case)
            (r'/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[%s]+)/'%(SLUGCHARS), specific),
            (r'/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>[%s]+)(?P<atom>\.atom)'%(SLUGCHARS), specific),
            (r'/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<atom>index\.atom)?', specific),
            (r'/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/', specific),
            (r'/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<atom>index\.atom)?', month),
            (r'/(?P<year>\d{4})/(?P<atom>index\.atom)?', year),
            (r'/(?P<atom>index\.atom)?', index)
        )



